import type { TableRecord } from "/@/api/models";
import { loopItem } from "/@/utils/treeData";
import type { BodyCellOptions } from "/@/types/antd";
import { findParent, getElRelativeXY } from "/@/utils/dom";
import { Ref } from "vue";

interface RowBox {
  left: number;
  top: number;
  height: number;
  el: HTMLTableRowElement;
  index: number;
}

let dragPlaceholder: HTMLDivElement | null = null;
function initDragPlaceholder(bodyEl: HTMLElement) {
  const width = bodyEl.offsetWidth + "px";
  if (dragPlaceholder) {
    dragPlaceholder.style.display = "block";
    dragPlaceholder.style.width = width;
    dragPlaceholder.setAttribute("data-end", "0");
  } else {
    dragPlaceholder = document.createElement("div") as HTMLDivElement;
    Object.assign(dragPlaceholder.style, {
      position: "absolute",
      background: "#4285F4",
      opacity: "0",
      zIndex: "9998",
      height: "2px",
      transition: "opacity .2s",
      width,
      left: "-9999px",
      top: "-9999px",
    });
    document.body.appendChild(dragPlaceholder);
    dragPlaceholder.addEventListener("transitionend", () => {
      if ((dragPlaceholder as HTMLDivElement).getAttribute("data-end") === "1") {
        (dragPlaceholder as HTMLDivElement).style.display = "none";
        (dragPlaceholder as HTMLDivElement).setAttribute("data-end", "0");
      }
    });
  }
}

const ROOT_LEVEL_CLASS = "ant-table-row-level-0";

function removeElByCls(el: HTMLElement, cls: string) {
  const target = el.querySelector("." + cls);
  if (target) {
    target.parentNode?.removeChild(target);
  }
}

export function useRowDragSort<T = TableRecord>({
  adjustNow,
  onSort,
  rowKey,
  data,
}: {
  adjustNow: boolean;
  onSort?: (curRecord: T, targetRecord: T, isUp: boolean, curIndex: number, targetIndex: number) => void;
  rowKey?: string;
  data: Ref<T[]>;
}) {
  let startData: {
    index: number;
    startX: number;
    startY: number;
    startLeft: number;
    startTop: number;
    rowBoxList: RowBox[];
    tempDiv: HTMLDivElement;
    targetRowBox: RowBox | null;
    isUp: boolean;
    levelCls: string;
    rowKeyValue: string;
    bodyEl: HTMLDivElement;
  } | null;
  function sortStart(e: MouseEvent, index: number) {
    e.stopPropagation();
    //寻找要排序的行元素dom
    const tdEl = findParent(e.target as HTMLElement, (el) =>
      el.classList.contains("ant-table-cell")
    ) as HTMLTableCellElement;
    const rowEl = findParent(e.target as HTMLElement, (el) =>
      el.classList.contains("ant-table-row")
    ) as HTMLTableRowElement;
    const bodyEl = findParent(rowEl, (el) => el.classList.contains("ant-table-body")) as HTMLDivElement;
    //初始化标识拖动排序位置的占位dom
    initDragPlaceholder(bodyEl);
    //行层级class
    let levelCls = "";
    rowEl.classList.forEach((cls) => {
      if (/^ant-table-row-level-/.test(cls)) {
        levelCls = cls;
      }
    });
    //找到所有相邻的层级元素，即同一层的所有元素
    const body = rowEl.parentNode as Element;
    const rows = body.querySelectorAll("tr.ant-table-row") as NodeListOf<HTMLTableRowElement>;
    let adjacentRows: { el: HTMLTableRowElement; index: number }[] = [];
    let isAdjacent = false;
    for (let i = 0, len = rows.length; i < len; i++) {
      const el = rows[i];
      if (el.classList.contains(levelCls)) {
        adjacentRows.push({
          el,
          index: i,
        });
        if (el === rowEl) {
          isAdjacent = true;
        }
      } else if (levelCls !== ROOT_LEVEL_CLASS) {
        if (isAdjacent) {
          break;
        }
        //不是同一层
        adjacentRows = [];
      }
    }
    if (adjacentRows.length > 0) {
      const rowBoxList: RowBox[] = [];
      adjacentRows.forEach((item, i) => {
        const offset = getElRelativeXY(item.el);
        rowBoxList.push({
          left: offset.x,
          top: offset.y,
          height: item.el.offsetHeight,
          el: item.el,
          index: item.index,
        });
      });
      // const offset = getElRelativeXY(rowEl);
      const tempDiv = document.createElement("div") as HTMLDivElement;
      const startLeft = e.pageX - 10;
      const startTop = e.pageY - 10;
      Object.assign(tempDiv.style, {
        position: "absolute",
        left: startLeft + "px",
        top: startTop + "px",
        background: "#ffffff",
        zIndex: 9999,
        "max-width": "600px",
        overflow: "hidden",
        "white-space": "nowrap",
        "border-radius": "8px",
      });
      tempDiv.className = "shadow-md";
      tempDiv.innerHTML = `<table style='width: auto; min-width: 100%; table-layout: fixed;'>
        <tbody class='ant-table-tbody'>
          <tr class='ant-table-row'>${tdEl.outerHTML}</tr>
        </tbody>
      </table>`;
      removeElByCls(tempDiv, "ant-table-row-indent");
      removeElByCls(tempDiv, "ant-table-row-expand-icon");
      document.body.appendChild(tempDiv);
      document.body.style.userSelect = "none";
      startData = {
        index,
        tempDiv,
        startX: e.pageX,
        startY: e.pageY,
        startLeft,
        startTop,
        rowBoxList,
        targetRowBox: null,
        isUp: false,
        levelCls,
        rowKeyValue: rowEl.getAttribute("data-row-key") as string,
        bodyEl,
      };
      document.addEventListener("mousemove", sortMove);
      document.addEventListener("mouseup", sortEnd);
    }
  }
  function sortMove(e: MouseEvent) {
    if (startData && dragPlaceholder) {
      const { startX, startY, tempDiv, startLeft, startTop, rowBoxList, index, bodyEl } = startData;
      const scrollTop = bodyEl.scrollTop;
      const dx = e.pageX - startX;
      const dy = e.pageY - startY;
      tempDiv.style.left = startLeft + dx + "px";
      tempDiv.style.top = startTop + dy + "px";
      let target: RowBox | null = null;
      for (let i = 0; i < rowBoxList.length; i++) {
        const rowBox = rowBoxList[i];
        if (e.pageY >= rowBox.top - scrollTop && e.pageY <= rowBox.top - scrollTop + rowBox.height) {
          if (rowBox.index !== index) {
            target = rowBox;
            break;
          }
        }
      }
      startData.targetRowBox = null;
      if (target) {
        dragPlaceholder.style.left = target.left + "px";
        let top: number;
        if (e.pageY <= target.top - scrollTop + target.height / 2) {
          if (target.index === index + 1) {
            dragPlaceholder.style.opacity = "0";
            return;
          }
          top = target.top - scrollTop - 1;
          startData.isUp = true;
        } else {
          if (target.index === index - 1) {
            dragPlaceholder.style.opacity = "0";
            return;
          }
          top = target.top - scrollTop + target.height;
          startData.isUp = false;
        }
        dragPlaceholder.style.top = top + "px";
        dragPlaceholder.style.opacity = "1";
        startData.targetRowBox = target;
      } else {
        dragPlaceholder.style.opacity = "0";
      }
    }
  }
  function isValueExist(value: any, obj: TableRecord, key?: string) {
    if (key) {
      return obj[key] === value;
    }
    for (let i in obj) {
      if (obj[i] === value) {
        return true;
      }
    }
    return false;
  }
  function sortEnd(e) {
    if (startData) {
      const { isUp, tempDiv, targetRowBox, levelCls, rowKeyValue } = startData;
      if (targetRowBox) {
        const targetRowKeyValue = targetRowBox.el.getAttribute("data-row-key");
        let curData: T[] = [],
          curIndex = -1,
          targetIndex = -1;
        if (levelCls === ROOT_LEVEL_CLASS) {
          curData = data.value;
          for (let i = 0; i < curData.length; i++) {
            if (isValueExist(rowKeyValue, curData[i], rowKey)) {
              curIndex = i;
            } else if (isValueExist(targetRowKeyValue, curData[i], rowKey)) {
              // console.log("=====>targetRowKeyValue", rowKeyValue, i, curData[i]);
              targetIndex = i;
            }
          }
        } else {
          //拖动的是子孙层
          loopItem(data.value, (item, i, items) => {
            if (isValueExist(rowKeyValue, item, rowKey)) {
              curData = items;
              curIndex = i;
            } else if (isValueExist(targetRowKeyValue, item, rowKey)) {
              targetIndex = i;
            }
            if (curIndex !== -1 && targetIndex !== -1) {
              return false;
            }
          });
        }
        onSort && onSort(curData[curIndex], curData[targetIndex], isUp, curIndex, targetIndex);
        //A B C D E F G
        if (adjustNow) {
          const record = curData.splice(curIndex, 1)[0];
          let _targetIndex = targetIndex;
          if (_targetIndex > curIndex) {
            _targetIndex--;
          }
          curData.splice(isUp ? _targetIndex : _targetIndex + 1, 0, record);
        }
      }
      document.body.removeChild(tempDiv);
      startData = null;
    }
    if (dragPlaceholder) {
      dragPlaceholder.setAttribute("data-end", "1");
      dragPlaceholder.style.opacity = "0";
    }
    document.removeEventListener("mousemove", sortMove);
    document.removeEventListener("mouseup", sortEnd);
    document.body.style.userSelect = "text";
  }

  return {
    sortStart,
  };
}
